 /*
 * Ingenic IMP FrameSource.
 *
 * Copyright (C) 2017 Ingenic Semiconductor Co.,Ltd
 */

#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <semaphore.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/prctl.h>
#include <linux/videodev2.h>
#include <linux/v4l2-mediabus.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pthread.h>

#include <isp/ncu_common.h>
#include "ncu_reg.h"
#include "ncuopt.h"
#include "ncu.h"
#include "ncu_param.h"
#include "ncu_ctrl.h"
#include "ncu_func.h"
#include "ncubuf.h"
#ifdef DMALLOC
#include <imp/dmalloc.h>
#endif
extern int read_reg_32(uint32_t addr, uint32_t *value);
extern int write_reg_32(uint32_t addr, uint32_t value);

int32_t hal_read_reg(uint32_t addr, uint32_t *value)
{
	return read_reg_32(addr, value);
}

int32_t hal_write_reg(uint32_t addr, uint32_t value)
{
	return write_reg_32(addr, value);
}

void reg_vic_write(uint32_t off, uint32_t value)
{
    hal_write_reg(VIC_BASE+off, value);
}

uint32_t reg_vic_read(uint32_t off)
{
	uint32_t value;
	hal_read_reg(VIC_BASE+off, &value);
    return value;
}

void reg_ncu_write(uint32_t off, uint32_t value)
{
    hal_write_reg(NCU_BASE+off, value);
}

uint32_t reg_ncu_read(uint32_t off)
{
	uint32_t value;
	hal_read_reg(NCU_BASE+off, &value);
    return value;
}

void reg_msca_write(uint32_t off, uint32_t value)
{
    hal_write_reg(MSCA_BASE+off, value);
}

uint32_t reg_msca_read(uint32_t off)
{
	uint32_t value;
	hal_read_reg(MSCA_BASE+off, &value);
    return value;
}

void reg_cpm_write(uint32_t off, uint32_t value)
{
	hal_write_reg(CPM_BASE+off, value);
}

uint32_t reg_cpm_read(uint32_t off)
{
	uint32_t value;
	hal_read_reg(CPM_BASE+off, &value);
    return value;
}

int ncu_load_param(char *file, ncu_params *nps)
{
	int ret = 0;
	FILE *fp = NULL;
	fp = fopen(file, "r");
	if (NULL == fp) {
		printf("err: fopen fp err\n");
		return -1;
	}

	ret = fread(nps, sizeof(ncu_params), 1, fp);
	if (1 != ret) {
		printf("error: ncubin file %s, read error %s\n", file, strerror(errno));
		return -1;
	}
	fclose(fp);
	return 0;
}

extern int ncudbg_server_start(char *ip, char *port);
int g_frame_get_sta_flag = 0;

int32_t ncu_hal_init(char *sensor_name, unsigned int width, unsigned int height)
{
	int ret = 0;
	/* ncu */
	ncu_opt nopt = {
		.dmain = 0,
		.dmaout = 0,
		.way_sel = 0,
		.frm_w = width,
		.frm_h = height,
		.frm_num = 10,
		.funcs = 0x000000,
		.src_file = NULL,
		.dst_file = NULL,
		.ref_file = NULL,
		.check = 0,
		.test_case = 100,
	};

	char ip[20] = {0};
	char port[20] = {0};
	int startserver = 0;
	if (access("/tmp/ncudbg.cfg", R_OK) == 0) {
		char *buf = NULL;
		char *str = NULL;
		char *linebuf = NULL;
		char *tmpbuf1 = NULL;
		char *tmpbuf2 = NULL;
		FILE *fp = NULL;
		buf = malloc(1024*3);
		if (NULL == buf) {
			printf("err: malloc err\n");
			return -1;
		}
		memset(buf, 0, 1024);
		linebuf = buf;
		tmpbuf1 = buf+1024*1;
		tmpbuf2 = buf+1024*2;

		fp = fopen("/tmp/ncudbg.cfg", "r");
		if (NULL == fp) {
			printf("err: fopen fp err\n");
			return -1;
		}
		while (NULL != (str = fgets(linebuf, 1024, fp))) {
			if (strlen(str) > 1000) {
				printf("err: str too length\n");
				return -1;
			} else if (2 != sscanf(linebuf, "%[^=]=%s", tmpbuf1, tmpbuf2)) {
				printf("warn: skip config1  %s\n", linebuf);
				continue;
			}
			if (1 != sscanf(tmpbuf1, "%s", tmpbuf1)) {
				printf("warn: skip config2  %s\n", linebuf);
				continue;
			}
			printf("%s, %s\n", tmpbuf1, tmpbuf2);
			if (0 == strcmp(tmpbuf1, "funcs")) {
				uint32_t data = 0;
				data = strtol(tmpbuf2, NULL, 0);
				nopt.funcs = data;
				printf("info ncu funcs = 0x%x\n", nopt.funcs);
			} else if (0 == strcmp(tmpbuf1, "ip")) {
				strncpy(ip, tmpbuf2, 16);
				startserver++;
			} else if (0 == strcmp(tmpbuf1, "port")) {
				strncpy(port, tmpbuf2, 16);
				startserver++;
			}
		}
		fclose(fp);
	}
	ncu_info *pncuinfo;
	//ncu_init();
	pncuinfo = (ncu_info*)ncu_get_info();
	ncu_config(&nopt);
	ncu_all_buf_alloc(pncuinfo);
	ncu_all_buf_config(pncuinfo, 0);

	ncu_func_config(nopt.frm_w, nopt.frm_h);
	//ncu_config_ready();
	//ncu_start();
	ncu_ctrl_init();

	char ncu_config_file[64] = {0};
	snprintf(ncu_config_file, 63,"/etc/sensor/ncu_%s.bin", sensor_name);
	if (access(ncu_config_file, R_OK) == 0) {
		ncu_params nps;
		ret = ncu_load_param(ncu_config_file, &nps);
		if (0 != ret) {
			printf("err: ncu_load_param\n");
		} else {
			ncu_set_params(&nps);
			ncu_ctrl_update_param();
		}
	} else {
		printf("warn: do not have %s\n", ncu_config_file);
	}
	if (2 == startserver) {
		printf("info: ip-%s, port-%s\n", ip, port);
		ncudbg_server_start(ip, port);
	}
	if (access("/tmp/ncu_sta_get_flag", R_OK) == 0) {
		g_frame_get_sta_flag = 1;
	}
	return 0;
}

int ncu_get_sta_data(int frame_num, void *data)
{
	int i = 0;
	ncu_info *pncuinfo;
	pncuinfo = (ncu_info*)ncu_get_info();
	uint8_t *buf = (uint8_t*)pncuinfo->sta_buf.y_addr;
	uint32_t size = pncuinfo->sta_buf.ysize;
	uint32_t cnt = pncuinfo->sta_buf.cnt;
	if (NULL == data) {
		printf("error(%s, %d): ptr data is null, frame_num = %d\n", __func__, __LINE__, frame_num);
		return -1;
	}
	for (i = 0; i < cnt; i++) {
		if (*(uint32_t*)(buf+size*i) == frame_num) {
			//printf("--------%d---------\n", frame_num);
			uint8_t *sta_y_buf = buf+size*i+pncuinfo->sta_attr.sta_y_stride;
			uint32_t sta_y_buf_size = size-pncuinfo->sta_attr.sta_y_stride;
			if (1 == g_frame_get_sta_flag)
				memcpy(data, sta_y_buf, sta_y_buf_size);
			break;
		}
	}
	if (i == cnt) {
		goto error;
	}
	return 0;
error:
	if (frame_num != 1) {
		//printf("warn frm_num = %d\n", frame_num);
	}
	return -1;
}

int ncu_get_frame_info(IMPNCUFrameInfo *info)
{
	ncu_info *pncuinfo;
	pncuinfo = (ncu_info*)ncu_get_info();
	info->width = pncuinfo->src_attr.frm_w;
	info->height = pncuinfo->src_attr.frm_h;
	info->sta_y_block_size = 28;
	info->sta_y_stride = pncuinfo->sta_attr.sta_y_stride;
	//buf size sub one line, filled with frame num
	info->sta_y_buf_size = pncuinfo->sta_buf.ysize-pncuinfo->sta_attr.sta_y_stride;
	info->sta_y_buf = 0;
	return 0;
}
